#!/usr/bin/env node
/**
 * Component coverage report
 * Usage: node scripts/component-coverage-report.js ComponentName
 */

const fs = require('fs');
const path = require('path');
const ts = require('typescript');

const COMPONENT_NAME = process.argv[2];

if (!COMPONENT_NAME) {
  console.error('❌ 错误: 缺少组件名称');
  console.error('用法: node scripts/component-coverage-report.js ComponentName');
  process.exit(1);
}

const ROOT = process.cwd();
const FIELD_FILE = path.join(ROOT, 'src/components/editor/schema/fields/aiComponents.ts');
const EDIT_FILE = path.join(ROOT, 'src/components/editor/schema/edit/aiComponents.ts');
const PREVIEW_FILE = path.join(
  ROOT,
  'src/components/editor/schema/preview/components',
  COMPONENT_NAME,
  'index.tsx',
);

const GLOBAL_KEYS = new Set(['visible', 'groupSetting', 'publishInfo', 'eventReportSetting', 'applyRules']);

function parseSource(filePath, kind) {
  const content = fs.readFileSync(filePath, 'utf8');
  return ts.createSourceFile(filePath, content, ts.ScriptTarget.ESNext, true, kind);
}

function collectFieldKeys() {
  const source = parseSource(FIELD_FILE, ts.ScriptKind.TS);
  const keys = new Set();

  function visit(node) {
    if (
      ts.isObjectLiteralExpression(node) &&
      node.properties?.some(
        (prop) =>
          ts.isPropertyAssignment(prop) &&
          prop.name?.getText() === 'type' &&
          ts.isStringLiteral(prop.initializer) &&
          prop.initializer.text === COMPONENT_NAME,
      )
    ) {
      const propsNode = node.properties.find(
        (prop) =>
          ts.isPropertyAssignment(prop) &&
          prop.name?.getText() === 'props' &&
          ts.isObjectLiteralExpression(prop.initializer),
      );
      if (propsNode) {
        propsNode.initializer.properties.forEach((prop) => {
          if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
            keys.add(prop.name.text);
          }
        });
      }
    }
    ts.forEachChild(node, visit);
  }

  ts.forEachChild(source, visit);
  return keys;
}

function collectEditKeys() {
  const source = parseSource(EDIT_FILE, ts.ScriptKind.TS);
  const belongTo = new Set();

  function visit(node) {
    if (
      ts.isPropertyAssignment(node) &&
      node.name?.getText() === COMPONENT_NAME &&
      ts.isArrayLiteralExpression(node.initializer)
    ) {
      node.initializer.elements.forEach((element) => {
        if (ts.isObjectLiteralExpression(element)) {
          element.properties.forEach((prop) => {
            if (ts.isPropertyAssignment(prop) && prop.name?.getText() === 'belongTo') {
              if (ts.isStringLiteral(prop.initializer)) {
                belongTo.add(prop.initializer.text);
              }
            }
          });
        }
      });
    }
    ts.forEachChild(node, visit);
  }

  ts.forEachChild(source, visit);
  return belongTo;
}

function readFileIfExists(filePath) {
  if (!fs.existsSync(filePath)) return '';
  return fs.readFileSync(filePath, 'utf8');
}

function collectRightFiles() {
  const rightDir = path.join(ROOT, 'src/components/editor/Right');
  const matches = [];
  function walk(dir) {
    fs.readdirSync(dir).forEach((entry) => {
      const abs = path.join(dir, entry);
      const stat = fs.statSync(abs);
      if (stat.isDirectory()) {
        walk(abs);
      } else if (entry.includes(`render${COMPONENT_NAME}`) && entry.endsWith('.tsx')) {
        matches.push(abs);
      }
    });
  }
  walk(rightDir);
  return matches;
}

const fieldKeys = collectFieldKeys();
if (fieldKeys.size === 0) {
  console.warn(`⚠️  未在 fields/aiComponents.ts 中找到 ${COMPONENT_NAME} 的配置`);
  process.exit(0);
}

const editBelongTo = collectEditKeys();
const previewContent = readFileIfExists(PREVIEW_FILE);
const rightFiles = collectRightFiles();
const rightContents = rightFiles.map(readFileIfExists);

const effectiveFieldKeys = Array.from(fieldKeys).filter((key) => !GLOBAL_KEYS.has(key));
const previewUsed = new Set();
const rightUsed = new Set();

effectiveFieldKeys.forEach((key) => {
  const regex = new RegExp(`\\b${key}\\b`, 'g');
  if (regex.test(previewContent)) {
    previewUsed.add(key);
  }
  if (
    rightContents.some((content) => regex.test(content))
  ) {
    rightUsed.add(key);
  }
});

const editCoverage = effectiveFieldKeys.length
  ? Math.round(((editBelongTo.size > 0 ? effectiveFieldKeys.length : 0) / effectiveFieldKeys.length) * 100)
  : 100;

console.log(`📊 ${COMPONENT_NAME} Coverage Report`);
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log(`Field props count       : ${fieldKeys.size}`);
console.log(`Effective props         : ${effectiveFieldKeys.length}`);
console.log(`Preview references      : ${previewUsed.size}`);
console.log(`Right render references : ${rightUsed.size}`);
console.log(`Edit belongTo count     : ${editBelongTo.size}`);
console.log(`Effective props         : ${effectiveFieldKeys.length}`);
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');

const missingInPreview = effectiveFieldKeys.filter((key) => !previewUsed.has(key));
const missingInRight = effectiveFieldKeys.filter((key) => !rightUsed.has(key));

if (missingInPreview.length) {
  console.log(`⚠️  Preview 未引用字段 (${missingInPreview.length}): ${missingInPreview.join(', ')}`);
}
if (missingInRight.length) {
  console.log(`⚠️  Right 未引用字段 (${missingInRight.length}): ${missingInRight.join(', ')}`);
}
if (!missingInPreview.length && !missingInRight.length) {
  console.log('✓ FieldNode 字段在 Preview 与 Right 中均有引用（或已列为全局字段）');
}
